import { isObjectValueEqual } from '@/utils/obj/equal';
import React, { useContext } from 'react';
import FieldContext, { HOOK_MARK } from './Context/FieldContext';
import ListContext from './Context/ListContext';
import type {
  FormInstance,
  InternalFormInstance,
  InternalHooks,
  Store,
  StoreValue,
} from './interface';
import type {
  FieldData,
  FieldEntity,
  FieldProps,
  InternalFieldProps,
  Meta,
  MetaEvent,
  NotifyInfo,
  ShouldUpdate,
} from './interface/FieldEntityType';
import type {
  FieldError,
  Rule,
  RuleError,
  RuleObject,
} from './interface/ValidateType';
import { get } from './utils/get';
import { validateRulesFn } from './utils/validateUtil';
import { defaultGetValueFromEvent, nameToNamePath } from './utils/valueUtil';

// Field没有设置shouldUpdate时通过preValue!==currValue来决定是否reRender
function requireUpdate(
  shouldUpdate: ShouldUpdate | undefined,
  prev: Store,
  next: Store,
  prevValue: StoreValue,
  currValue: StoreValue,
  info: NotifyInfo,
): boolean {
  if (typeof shouldUpdate === 'function') {
    return shouldUpdate(
      prev,
      next,
      'source' in info ? { source: info.source } : {},
    );
  }
  if (typeof shouldUpdate === 'boolean') {
    return shouldUpdate;
  }

  return prevValue !== currValue;
}

interface FieldState {
  resetCount: number;
}

class Field
  extends React.Component<InternalFieldProps, FieldState>
  implements FieldEntity
{
  //1. public static contextType = FieldContext; this.context === (useContext(FiledContext))
  //2. <FieldContext.Consumer> { (internalFormInstance) => <Field internalFormInstance={internalFormInstance} /> } </ FieldContext.Consumer>
  public static defaultProps = {
    trigger: 'onChange',
    valuePropName: 'value',
  };

  // classComponent 只有setState()时，函数才会自动执行render函数，否则就只能通过forceUpdate手动执render函数
  public state = {
    resetCount: 0,
  };

  private mounted: boolean = false;
  private touched: boolean = false;
  private dirty: boolean = false;

  /**
   *
   *
   * @private
   * @type {(Promise<string[]> | null | undefined)} undefined:初始化状态，Promise<string[]> validating，null validated
   * @memberof Field
   */
  private validatePromise: Promise<FieldError> | null | undefined;

  private errors: React.ReactNode[] = [];
  private warnings: React.ReactNode[] = [];

  private metaCache: MetaEvent | null = null;

  // ============================== Subscriptions ==============================

  constructor(props: InternalFieldProps) {
    super(props);
    // console.log(`----1.name:${props.name}---reset时 constructor重新执行----`);
    if (props.fieldContext) {
      const { initEntityValue } = props.fieldContext.getInternalHooks(
        HOOK_MARK,
      ) as InternalHooks;
      initEntityValue(this);
    }
  }

  public componentDidMount() {
    // console.log(
    //   `----4.name:${this.props.name}---reset时 componentDidMount会执行----`,
    // );
    this.mounted = true;
    const { shouldUpdate, fieldContext } = this.props;
    const { registerField } = fieldContext.getInternalHooks(
      HOOK_MARK,
    ) as InternalHooks;
    registerField(this);
    if (shouldUpdate === true) {
      this.reRender();
    }
  }

  public componentWillUnmount() {
    // console.log(
    //   `----3.name:${this.props.name}---reset时 componentWillUnmount会执行----`,
    // );
    this.mounted = false;
    this.triggerMetaEvent(true);
    const { cancelField } = this.props.fieldContext.getInternalHooks(
      HOOK_MARK,
    ) as InternalHooks;
    cancelField(this);
  }

  // ========================= Field Entity Interfaces =========================
  /**
   *
   *@param updateNamePathList: 本次操作影响Field的范围，info.type === 'dependenciesUpdate'除外，
   * @type {FieldEntity['onStoreChange']}
   * @memberof Field
   * @description 1.凡是执行updateStore(setInitialValues+initEntityValue 中的updateStore 最后在registerField中 执行到onStoreChange ) 的地方就得执行onStoreChange
   * @description 2.Field没有设置shouldUpdate时通过preValue!==currValue来决定是否reRender, 此时Field[undefined]preValue===currValue===undefined,不会执行reRender
   * @description 3.info.type === dependenciesUpdate / resetFields ? 当前Field refresh/reRender, 其余字段不reRender : 当前Field reRender,其余字段根据prevValue!==currValue  /shouldUpdate来判断是否reRender
   */
  public onStoreChange: FieldEntity['onStoreChange'] = (
    prevStore,
    updateNamePathList,
    info,
  ) => {
    const namePath = this.getNamePath();

    const { shouldUpdate, isList, dependencies = [] } = this.props; // List已经定义了shouldUpdate函数
    const { type, store } = info;
    const preValue = this._getValue(prevStore);
    const currValue = this._getValue(store);
    const currNamePathInUpdateNamePathList =
      updateNamePathList.findIndex(
        (updateNaemPath) => updateNaemPath.join(',') === namePath.join(','),
      ) > -1
        ? true
        : false;

    //setFieldsValue({ user:{name:'zyj'}, users:[{name:'zyj'}] }) api触发，此时 updateNamePathList===[],每个Field都要执行shouldUpdate来决定是否需要reRender
    if (type === 'valueUpdate' && info.source === 'external') {
      if (preValue !== currValue) {
        this.touched = true;
        this.dirty = true;
        this.validatePromise = null;
        this.errors = [];
        this.warnings = [];
        this.triggerMetaEvent();
      }
      if (isList && (shouldUpdate as Function)(preValue, currValue, {})) {
        this.reRender();
      } else if (
        requireUpdate(shouldUpdate, prevStore, store, preValue, currValue, info)
      ) {
        this.reRender();
      }

      //setFieldValue(['users',0,'name'],'zyj') / setFields([ {name:['users',0,'name'],value:'zyj' } ])
    } else if (type === 'setField') {
      if (currNamePathInUpdateNamePathList) {
        const { data } = info;
        const currNamePathFieldData = data.get(namePath.join(',')) as FieldData;
        if ('touched' in currNamePathFieldData) {
          this.touched = !!currNamePathFieldData.touched;
        }
        if ('validating' in currNamePathFieldData) {
          this.validatePromise = currNamePathFieldData.validating
            ? Promise.resolve({ name: namePath, errors: [], warnings: [] })
            : undefined;
        }
        if ('errors' in currNamePathFieldData) {
          this.errors = currNamePathFieldData.errors || [];
        }
        if ('warnings' in currNamePathFieldData) {
          this.warnings = currNamePathFieldData.warnings || [];
        }
        this.dirty = true;
        this.triggerMetaEvent();
        this.reRender();
      } else if (
        !isList &&
        requireUpdate(shouldUpdate, prevStore, store, preValue, currValue, info)
      ) {
        //其余字段执行shouldUpdate来决定是否需要reRender
        this.reRender();
      }
      // updateValue(onChange) 、 registerField 时 updateNamePathList ===[]
    } else if (type === 'valueUpdate' && info.source === 'internal') {
      if (isList) {
        if (currNamePathInUpdateNamePathList) {
          this.reRender();
        } else if (
          (shouldUpdate as Function)(prevStore, store, { source: 'internal' })
        ) {
          //永远是false
          this.reRender();
        }
      } else if (
        currNamePathInUpdateNamePathList ||
        requireUpdate(shouldUpdate, prevStore, store, preValue, currValue, info)
      ) {
        this.reRender();
      }

      // cancelField updateNamePathList[0]就是本次被删的Field， 如果当前Field.isListField===true,就不会触发remove
    } else if (type === 'remove') {
      if (isList && (shouldUpdate as Function)(preValue, currValue, {})) {
        this.reRender();
      } else if (
        requireUpdate(shouldUpdate, prevStore, store, preValue, currValue, info)
      ) {
        this.reRender();
      }

      //  updateValue(onChange) / cancelField, dependencies['name'] 触发，(dependencies不要和shouldUpdate同时设置在一个Field上，因为，updateValue(onChange)/cancelFiel会同时触发type='valueUpdate'和dependenciesUpdate ) ,其余字段不需要reRender
    } else if (type === 'dependenciesUpdate') {
      // const depTargetNamePath = updateNamePathList[0];
      updateNamePathList.forEach((namePathListItem) => {
        const depIndex = dependencies.findIndex(
          (depName) =>
            nameToNamePath(depName).join(',') === namePathListItem.join(','),
        );
        if (depIndex > -1) {
          this.reRender();
        }
      });

      //resetFields(['name',['users',0,'name']]) 其余字段不需要reRender
    } else if (type === 'reset') {
      if (
        !updateNamePathList ||
        !updateNamePathList.length ||
        currNamePathInUpdateNamePathList
      ) {
        // Clean up state
        this.touched = false;
        this.dirty = false;
        this.validatePromise = null;
        this.errors = [];
        this.warnings = [];
        this.triggerMetaEvent();
        this.refresh();
      }
    }
    // else if (type === 'validateFinish') {
    //   if (!updateNamePathList || !updateNamePathList.length || currNamePathInUpdateNamePathList) {
    //     this.reRender();
    //   } else if (requireUpdate(shouldUpdate, prevStore, store, preValue, currValue, info)) {
    //     this.reRender();
    //   }
    // }
  };

  public isFieldListField: FieldEntity['isFieldListField'] = () =>
    !!this.props.isListField;

  public isFieldList: FieldEntity['isFieldList'] = () => !!this.props.isList;

  public validateRules: FieldEntity['validateRules'] = (options) => {
    const {
      validateMessages,
      validateOnly = false,
      triggerName,
    } = options || {};
    const { validateFirst = false, messageVariables } = this.props;
    const namePath = this.getNamePath();
    const currValue = this._getValue();

    const rootPromise: Promise<FieldError> = new Promise((resolve, reject) => {
      let filteredRules = this.getRules();
      if (triggerName) {
        filteredRules = filteredRules.filter(
          (rule) => rule && Object.keys(rule).length > 0,
        );
        filteredRules = filteredRules.filter((rule) => {
          const { validateTrigger } = rule;
          if (!validateTrigger) {
            return true;
          } else if (
            (Array.isArray(validateTrigger)
              ? validateTrigger
              : [validateTrigger]
            ).indexOf(triggerName) > -1
          ) {
            return true;
          }
        });
      }
      const validatePromise = validateRulesFn(
        namePath,
        currValue,
        filteredRules,
        { validateMessages },
        validateFirst,
        messageVariables,
      );
      //RuleError -- FieldError
      validatePromise
        .catch((ruleErrors: RuleError[] = []) => {
          return ruleErrors;
        })
        .then((ruleErrors: RuleError[]) => {
          // console.log(
          //   `--3.${namePath.join(
          //     ',',
          //   )} Filed 验证完毕--validating = false, validated = true--`,
          //   this.validatePromise === rootPromise,
          //   ruleErrors,
          // );
          this.validatePromise = null; //validating = false, validated = true, 验证完毕
          // Get errors & warnings
          const nextErrors: React.ReactNode[] = [];
          const nextWarnings: React.ReactNode[] = [];
          ruleErrors.forEach?.(({ rule: { warningOnly }, errors = [] }) => {
            if (warningOnly) {
              nextWarnings.push(...errors);
            } else {
              nextErrors.push(...errors);
            }
          });
          this.errors = nextErrors;
          this.warnings = nextWarnings;
          this.triggerMetaEvent();
          this.reRender();

          if (nextErrors.length > 0) {
            // console.log(`--4.${namePath.join(',')} Filed--验证不通过--`);
            reject({
              name: namePath,
              errors: nextErrors,
              warnings: nextWarnings,
            });
          } else {
            // console.log(`--4.1 ${namePath.join(',')} Filed--验证通过--`);
            resolve({ name: namePath, errors: [], warnings: nextWarnings });
          }
        });
    });

    if (validateOnly) {
      //仅校验内容而不会将错误信息展示到 UI 上
      return rootPromise;
    }

    this.validatePromise = rootPromise;
    // console.log(
    //   `--1.${namePath.join(
    //     ',',
    //   )} Filed验证开始：--validating = true,validated = false--:`,
    // );
    this.dirty = true;
    this.errors = [];
    this.warnings = [];
    this.triggerMetaEvent();
    this.reRender();
    return rootPromise;
  };

  //该Field执行过onChange 或者有initialValue
  public isFieldDirty = () => {
    // Touched or validate or has initialValue
    if (this.dirty || this.props.initialValue !== undefined) {
      return true;
    }

    // Form set initialValue
    const { fieldContext } = this.props;
    const internalHooks = fieldContext.getInternalHooks(
      HOOK_MARK,
    ) as InternalHooks;
    const { getInitialValue } = internalHooks;
    if (getInitialValue(this.getNamePath()) !== undefined) {
      return true;
    }

    return false;
  };

  // ============================= Child Component =============================
  public getMeta: FieldEntity['getMeta'] = () => {
    // Make error & validating in cache to save perf

    const meta: Meta = {
      touched: this.touched,
      validating: !!this.validatePromise,
      errors: this.errors,
      warnings: this.warnings,
      name: this.getNamePath(), //[...name,...prefixName]
      validated: this.validatePromise === null,
    };

    return meta;
  };
  public getErrors: FieldEntity['getErrors'] = () => [];

  public getWarnings: FieldEntity['getWarnings'] = () => [];

  // ============================== Field Control ==============================
  private _getValue = (store?: Store) => {
    const namePath = this.getNamePath();
    const { getFieldsValue } = this.props.fieldContext;
    return get(store || getFieldsValue('all'), namePath);
  };

  private _getControlled: (
    childProps?: Record<string, any>,
  ) => Record<string, any> = (childProps = {}) => {
    const {
      trigger = 'onChange',
      validateTrigger,
      fieldContext,
      rules,
      valuePropName = 'value',
      name,
    } = this.props;
    const { updateValue } = fieldContext.getInternalHooks(
      HOOK_MARK,
    ) as InternalHooks;
    const namePath = this.getNamePath();
    if (name === undefined || name === null || !namePath.length) {
      return childProps ?? {}; //!Field.name 就不是表单控件
    }
    const value = this._getValue(); //包含Form.initialValues / Field.initialValue

    const finallChildProps: Record<string, any> = {
      ...childProps,
      [valuePropName]: value, // value ?? '', value:undefined 当input的值被初始化为undefined，但后来又变更为一个不同的值时，会产生"A component is changing an uncontrolled input to be controlled"警告
    };
    finallChildProps[trigger] = (...args: any) => {
      //第一次重新定义
      // Mark as touched
      this.touched = true;
      this.dirty = true;
      this.triggerMetaEvent();
      const newValue = defaultGetValueFromEvent(valuePropName, args[0]);
      updateValue(namePath, newValue);
      if (typeof childProps[trigger] === 'function') {
        childProps[trigger](...args);
      }
    };

    //...validatet
    const { validateTrigger: fcValidateTrigger } = fieldContext;
    const mergeValidateTrigger = validateTrigger ?? fcValidateTrigger;
    const mergeValidateTriggers = Array.isArray(mergeValidateTrigger)
      ? mergeValidateTrigger
      : [mergeValidateTrigger];
    mergeValidateTriggers.forEach((triggerName) => {
      const originTrigger = finallChildProps[triggerName];
      finallChildProps[triggerName] = (...args: any) => {
        //第二次重新定义
        if (originTrigger) {
          originTrigger(...args);
        }
        if (rules && rules.length) {
          //注：prmose.catch必须要执行不然报错：FormStore.ts:542 Uncaught (in promise) [promise中未捕获的错误]
          fieldContext
            .validateFields([namePath], { triggerName })
            .catch((err) => {
              console.log('----onChange--validateFields--catch--err--:', err);
            });
        }
      };
    });

    return finallChildProps;
  };

  private _getOnlyChild: (
    child?: React.ReactNode,
  ) => React.ReactElement | null = (child) => {
    if (!child) {
      return null;
    }

    const childs: React.ReactNode[] = React.Children.toArray(child); //会过滤掉 null，undefined
    if (childs.length > 1) {
      console.error('Filed 的children 不能是数组！');
    }
    return React.isValidElement(childs[0]) ? childs[0] : null; //isValidElement只能判断单个节点，不能判断元素
  };

  render(): React.ReactNode {
    const { resetCount } = this.state;
    const { children } = this.props;
    // const childrens = React.Children.toArray(children)
    let finallChildren: React.ReactNode = null;
    if (typeof children === 'function') {
      finallChildren = children(
        this._getControlled(),
        this.getMeta(),
        this.props.fieldContext as FormInstance,
      );
      finallChildren = this._getOnlyChild(finallChildren);
    } else {
      finallChildren = this._getOnlyChild(children);
      if (finallChildren) {
        finallChildren = React.cloneElement(
          finallChildren,
          this._getControlled(finallChildren.props),
        );
      }
    }
    // console.log(
    //   `----2.name:${this.props.name}---render函数执行--resetCount----:`,
    //   resetCount,
    // );
    return <React.Fragment key={resetCount}>{finallChildren}</React.Fragment>;
  }

  // ================================== Utils ==================================
  public getNamePath: FieldEntity['getNamePath'] = () => {
    const { name, fieldContext } = this.props;
    const { prefixName = [] }: InternalFormInstance = fieldContext;

    return name !== undefined ? [...prefixName, ...name] : [];
  };

  public getRules = (): RuleObject[] => {
    const { rules = [], fieldContext } = this.props;

    return rules.map((rule: Rule): RuleObject => {
      if (typeof rule === 'function') {
        return rule(fieldContext);
      }
      return rule;
    });
  };

  // 组件不动，重新执行render函数
  public reRender() {
    if (!this.mounted) return;
    this.forceUpdate();
  }
  /**
   *
   * @memberof Field
   * @description  使用场景 <Field dependencies={['password']}> { () => ( <Field name="passwordConfirm"  /> ) } </Filed>
   * @description 调用setState(resetCount), 1.setState会触发Field[undefined]的render函数，2.resetCount改变Field['passwordConfirm']会重新加载
   */
  public refresh = () => {
    if (!this.mounted) return;
    /**
     * Clean up current node.
     */
    this.setState(({ resetCount }) => ({
      resetCount: resetCount + 1,
    }));
  };

  //只要meta改变就要执行该函数
  public triggerMetaEvent = (destroy?: boolean) => {
    const { onMetaChange } = this.props;

    if (onMetaChange) {
      const meta = { ...this.getMeta(), destroy };

      if (!isObjectValueEqual(this.metaCache, meta)) {
        onMetaChange(meta);
      }

      this.metaCache = meta;
    } else {
      this.metaCache = null;
    }
  };
}

function WrapperField(props: FieldProps) {
  const { name, isListField, ...restProps } = props;
  const listContext = useContext(ListContext);
  const fieldContext = useContext(FieldContext);
  const namePath = name !== undefined ? nameToNamePath(name) : undefined;
  let key;
  if (!isListField) {
    //List下的Fiel是有key的,不是数组下的Children,key是可以一样的
    key = `_${(namePath || []).join('_')}`; // '_'
  }

  const combineIsListField =
    typeof isListField === 'boolean' ? isListField : !!listContext;

  return (
    <Field
      key={key}
      name={namePath}
      isListField={combineIsListField}
      {...restProps}
      fieldContext={fieldContext}
    />
  );
}

export default WrapperField;
